<script lang="ts" setup>
import { i18n } from "@/locales";
import {
  NodeViewContent,
  NodeViewWrapper,
  type NodeViewProps,
} from "@/tiptap/vue-3";
import { useTimeout } from "@vueuse/core";
import { computed } from "vue";
import BxBxsCopy from "~icons/bx/bxs-copy";
import RiArrowDownSFill from "~icons/ri/arrow-down-s-fill";
import RiArrowRightSFill from "~icons/ri/arrow-right-s-fill";
import IconCheckboxCircle from "~icons/ri/checkbox-circle-line";
import CodeBlockSelect from "./CodeBlockSelect.vue";

const props = defineProps<NodeViewProps>();

const languageOptions = computed(() => {
  let languages: Array<{
    label: string;
    value: string;
  }> = [];
  const lang = props.extension.options.languages;
  if (typeof lang === "function") {
    languages = lang(props.editor.state);
  } else {
    languages = lang;
  }
  languages = languages || [];
  const languageValues = languages.map((language) => language.value);
  if (languageValues.indexOf("auto") === -1) {
    languages.unshift({
      label: "Auto",
      value: "auto",
    });
  }
  return languages;
});

const selectedLanguage = computed({
  get: () => {
    return props.node?.attrs.language || "auto";
  },
  set: (language: string) => {
    props.updateAttributes({ language: language });
  },
});

const themeOptions = computed(() => {
  let themes:
    | Array<{
        label: string;
        value: string;
      }>
    | undefined = [];
  const theme = props.extension.options.themes;
  if (typeof theme === "function") {
    themes = theme(props.editor.state);
  } else {
    themes = theme;
  }

  if (!themes) {
    return undefined;
  }
  return themes;
});

const selectedTheme = computed({
  get: () => {
    return props.node?.attrs.theme || themeOptions.value?.[0]?.value;
  },
  set: (theme: string) => {
    props.updateAttributes({ theme: theme });
  },
});

const collapsed = computed<boolean>({
  get: () => {
    return props.node.attrs.collapsed || false;
  },
  set: (collapsed: boolean) => {
    props.updateAttributes({ collapsed: collapsed });
  },
});

const { ready, start } = useTimeout(2000, { controls: true, immediate: false });

const handleCopyCode = () => {
  if (!ready.value) return;
  const code = props.node.textContent;
  navigator.clipboard.writeText(code).then(() => {
    start();
  });
};
</script>
<template>
  <node-view-wrapper as="div" class="code-node">
    <div contenteditable="false" class="code-header">
      <div
        class="code-header-left"
        @click.self="collapsed ? ((collapsed as any) = false) : null"
      >
        <div class="code-collapse-wrapper">
          <div
            class="code-collapse-button"
            @click.stop="(collapsed as any) = !collapsed"
          >
            <RiArrowRightSFill v-if="collapsed" />
            <RiArrowDownSFill v-else />
          </div>
        </div>
        <CodeBlockSelect
          v-model="selectedLanguage as any"
          class="code-select"
          container="body"
          :options="(languageOptions as any)"
        >
        </CodeBlockSelect>
        <CodeBlockSelect
          v-if="themeOptions && (themeOptions as any).length > 0"
          v-model="selectedTheme as any"
          container="body"
          class="code-select"
          :options="(themeOptions as any)"
          @select="editor.commands.focus()"
        >
        </CodeBlockSelect>
      </div>
      <div class="code-header-right">
        <div
          v-tooltip="
            ready
              ? i18n.global.t('editor.common.codeblock.copy_code')
              : i18n.global.t('editor.common.codeblock.copy_code_success')
          "
          class="code-copy-button"
          :class="{ 'can-hover': ready }"
          @click="handleCopyCode"
        >
          <IconCheckboxCircle v-if="!ready" class="code-icon success" />
          <BxBxsCopy v-else class="code-icon" />
        </div>
      </div>
    </div>
    <pre v-show="!collapsed"><node-view-content as="code" class="hljs" /></pre>
  </node-view-wrapper>
</template>

<style lang="scss" scoped>
.code-node {
  margin-top: 0.75rem; // mt-3
  overflow: hidden;
  border-radius: 0.25rem; // rounded
  border: 1px solid #e5e7eb; // border-[1px]
}

.code-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-bottom: 1px solid #f3f4f6; // border-b-[1px] border-b-gray-100
  background-color: #f5f5f5; // bg-neutral-100
  padding-top: 0.25rem; // py-1
  padding-bottom: 0.25rem;
}

.code-header-left {
  display: flex;
  flex: 1;
  align-items: center;
  padding-left: 0.75rem; // pl-3
}

.code-collapse-wrapper {
  display: flex;
  align-items: center;
  padding-right: 0.75rem; // pr-3
}

.code-collapse-button {
  display: flex;
  height: 2rem; // h-8
  width: 2rem; // w-8
  cursor: pointer;
  align-items: center;
  justify-content: center;
  border-radius: 0.25rem; // rounded

  &:hover {
    background-color: #e4e4e7; // hover:bg-zinc-200
  }
}

.code-select {
  width: 12rem; // w-48
}

.code-header-right {
  display: flex;
  align-items: center;
  padding-right: 0.75rem; // pr-3
}

.code-copy-button {
  display: flex;
  height: 2rem; // h-8
  width: 2rem; // w-8
  cursor: pointer;
  align-items: center;
  justify-content: center;
  border-radius: 0.25rem; // rounded

  &.can-hover:hover {
    background-color: #e4e4e7; // hover:bg-zinc-200
  }
}

.code-icon {
  height: 1rem; // h-4
  width: 1rem; // w-4
  color: #6b7280; // text-gray-500

  &.success {
    color: #22c55e; // text-green-500
  }
}
</style>
